import numpy as np
import matplotlib.pyplot as plt

def clip_lines(lines, rect):
    """
    裁剪线段的主函数
    :param lines: 包含线段的列表，每个线段由两个点组成
    :param rect: 裁剪矩形，由四个顶点定义
    :return: 裁剪后的线段列表
    """
    clipped_lines = []  # 存储裁剪后的线段

    for line in lines:
        start, end = line  # 线段的起点和终点
        x1, y1 = start
        x2, y2 = end

        # 使用 Nicholl-Lee-Nicholl 算法进行裁剪
        clip_line(x1, y1, x2, y2, rect, clipped_lines)

    return clipped_lines  # 返回裁剪后的线段列表


def clip_line(x1, y1, x2, y2, rect, clipped_lines):
    """
    使用 Nicholl-Lee 算法裁剪线段
    :param x1: 线段起点 x 坐标
    :param y1: 线段起点 y 坐标
    :param x2: 线段终点 x 坐标
    :param y2: 线段终点 y 坐标
    :param rect: 裁剪区域的矩形（以左下和右上角坐标表示）
    :param clipped_lines: 存储裁剪后的线段
    """
    x_min, y_min = rect[0]  # 裁剪矩形的左下角
    x_max, y_max = rect[2]  # 裁剪矩形的右上角

    # 初始化参数
    dx = x2 - x1
    dy = y2 - y1
    p = [-dx, dx, -dy, dy]
    q = [x1 - x_min, x_max - x1, y1 - y_min, y_max - y1]

    # 初始化
    t0, t1 = 0.0, 1.0

    for i in range(4):  # 对每个边进行测试
        if p[i] == 0:  # 线段与边平行
            if q[i] < 0:  # 如果线段在边的外面
                return  # 完全在矩形外，不绘制
        else:
            t = q[i] / p[i]
            if p[i] < 0:  # 进入边界
                t0 = max(t0, t)
            else:  # 离开边界
                t1 = min(t1, t)

    if t0 > t1:  # 如果参数区间无效
        return

    # 计算裁剪后的坐标
    clipped_start = (x1 + t0 * dx, y1 + t0 * dy)
    clipped_end = (x1 + t1 * dx, y1 + t1 * dy)

    # 添加裁剪后线段
    clipped_lines.append((clipped_start, clipped_end))


def plot_lines(lines, clipped_lines, rect):
    """
    可视化线段和裁剪矩形
    :param lines: 原始线段
    :param clipped_lines: 裁剪后的线段
    :param rect: 裁剪矩形
    """
    plt.figure(figsize=(8, 8))

    # 绘制裁剪矩形
    rectangle = plt.Rectangle((rect[0][0], rect[0][1]), rect[2][0] - rect[0][0], rect[2][1] - rect[0][1],
                               linewidth=2, edgecolor='blue', facecolor='none', label='Clipping Rectangle')
    plt.gca().add_patch(rectangle)

    # 绘制原始线段
    for line in lines:
        start, end = line
        plt.plot([start[0], end[0]], [start[1], end[1]], color='red', label='Original Line' if line == lines[0] else "")
    
    # 绘制裁剪后的线段
    for line in clipped_lines:
        start, end = line
        plt.plot([start[0], end[0]], [start[1], end[1]], color='green', linestyle='--', label='Clipped Line' if line == clipped_lines[0] else "")
        
    # 设置界限
    plt.xlim(0, 200)
    plt.ylim(0, 200)
    plt.axhline(0, color='black',linewidth=0.5, ls='--')
    plt.axvline(0, color='black',linewidth=0.5, ls='--')
    plt.grid(color = 'gray', linestyle = '--', linewidth = 0.5)
    plt.gca().set_aspect('equal', adjustable='box')
    plt.title('Line Clipping Visualization')
    plt.xlabel('X-axis')
    plt.ylabel('Y-axis')
    plt.legend()
    plt.show()


# 测试裁剪功能
if __name__ == "__main__":
    # 使用不完全保留的线段示例
    lines = [((40, 60), (160, 120)),  # 这条线段将被裁剪
             ((70, 40), (90, 90)),   # 这条线段完全保留
             ((160, 80), (130, 130))] # 这条线段部分被裁剪
    # 定义裁剪矩形
    rect = np.array([[50, 50],  # 左下角
                     [50, 150],  # 左上角
                     [150, 150],  # 右上角
                     [150, 50]])  # 右下角
                     
    clipped = clip_lines(lines, rect)
    print("Clipped Lines:", clipped)  # 输出裁剪后的线段

    # 可视化
    plot_lines(lines, clipped, rect)
